The ONE仿真器读取外部事件(StandardEventsReader
的readEvents
)存在BUG(感谢@馒头分享),本文描述并修复该BUG。
1. BUG描述
The ONE读取外部事件是由StandardEventsReader
的readEvents
完成。默认情况下,每次读取500个,可以在设置文件修改默认值,如下:
public static final int DEFAULT_NROF_PRELOAD = 500 //ExternalEventsQueue.java
//设置文件
Events1.class = ExternalEventsQueue
Events1.nrofPreload = …
readEvents
每次读取501个事件,500个放入events,另一个没有做任何处理,所以每500个事件会有1个漏掉,以下是我某次仿真的结果,可见实际创建的消息数目少于消息创建事件的数目。
#消息事件数目 #实际创建的消息数目
20000 199601
24000 239521
26000 259482
30000 299402
36000 359282
2. BUG分析及修复
(1)BUG分析
产生该的BUG原因是readEvents
先读取事件再判断循环条件,相关源代码如下:
// StandardEventsReader.java
public List<ExternalEvent> readEvents(int nrof) {
...
while (eventsRead < nrof && line != null) {
...
line = this.reader.readLine(); //问题在这里!
eventsRead++;
...
}
return events;
}
问题就出在第6行,先读取下一行,再判断eventsRead
是否小于nrof
,若不小于,则结束事件读取,这样就漏掉了一个事件。
(2)修复BUG
修改该BUG也简单,先对eventsRead
做判断再决定是否读取下一个事件,如下:
line = this.reader.readLine();
eventsRead++;
// 改成:
eventsRead++;
if (eventsRead < nrof) {
line = this.reader.readLine();
}
事实上,读取事件readEvents
完全可以用do-while
来做,代码会更加整洁,如下(注:以下代码没有测试):
// StandardEventsReader.java, fix the bug, events reader
public List<ExternalEvent> readEvents(int nrof) {
ArrayList<ExternalEvent> events = new ArrayList<ExternalEvent>(nrof);
int eventsRead = 0;
// skip empty and comment lines
Pattern skipPattern = Pattern.compile("(#.*)|(^\\s*$)");
String line;
do
{
/*** Step 1: read a line ***/
try {
line = this.reader.readLine();
} catch (IOException e1) {
throw new SimError("Reading from external event file failed.");
}
Scanner lineScan = new Scanner(line);
if (skipPattern.matcher(line).matches()) { // skip empty and comment lines
continue;
}
/*** Step 2: process the line ***/
double time;
String action;
String msgId;
int hostAddr;
int host2Addr;
try {
time = lineScan.nextDouble();
action = lineScan.next();
/*** drop message event ***/
if (action.equals(DROP)) { // public static final String DROP = "DR";
msgId = lineScan.next();
hostAddr = getHostAddress(lineScan.next());
events.add(new MessageDeleteEvent(hostAddr, msgId, time, true));
}
/*** remove message event ***/
else if (action.equals(REMOVE)) { // public static final String REMOVE = "R";
msgId = lineScan.next();
hostAddr = getHostAddress(lineScan.next());
events.add(new MessageDeleteEvent(hostAddr, msgId, time, false));
}
/*** connection event ***/
else if (action.equals(CONNECTION)) { // public static final String CONNECTION = "CONN";
String connEventType;
boolean isUp;
hostAddr = getHostAddress(lineScan.next());
host2Addr = getHostAddress(lineScan.next());
connEventType = lineScan.next();
String interfaceId = null;
if (lineScan.hasNext()) {
interfaceId = lineScan.next();
}
if (connEventType.equalsIgnoreCase(CONNECTION_UP)) {
isUp = true;
}
else if (connEventType.equalsIgnoreCase(CONNECTION_DOWN)) {
isUp = false;
}
else {
throw new SimError("Unknown up/down value '" + connEventType + "'");
}
ConnectionEvent ce = new ConnectionEvent(hostAddr, host2Addr, interfaceId, isUp, time);
events.add(ce);
}
else {
msgId = lineScan.next();
hostAddr = getHostAddress(lineScan.next());
host2Addr = getHostAddress(lineScan.next());
/*** create message event ***/
if (action.equals(CREATE)){ // public static final String CREATE = "C";
int size = lineScan.nextInt();
int respSize = 0;
if (lineScan.hasNextInt()) {
respSize = lineScan.nextInt();
}
events.add(new MessageCreateEvent(hostAddr, host2Addr, msgId, size, respSize, time));
}
else {
int stage = -1;
/*** send message event ***/
if (action.equals(SEND)) { // public static final String SEND = "S";
stage = MessageRelayEvent.SENDING;
}
/*** delivered message event ***/
else if (action.equals(DELIVERED)) { // public static final String DELIVERED = "DE";
stage = MessageRelayEvent.TRANSFERRED;
}
/*** abort message event ***/
else if (action.equals(ABORT)) { // public static final String ABORT = "A";
stage = MessageRelayEvent.ABORTED;
}
else {
throw new SimError("Unknown action '" + action + "' in external events");
}
events.add(new MessageRelayEvent(hostAddr, host2Addr, msgId, time, stage));
}
}
// discard the newline in the end
if (lineScan.hasNextLine()) {
lineScan.nextLine(); // TODO: test
}
}catch (Exception e) {
throw new SimError("Can't parse external event " + (eventsRead+1) + " from '" + line + "'", e);
}
eventsRead++;
} while (eventsRead < nrof && line != null);
return events;
}